package com.game.core.net.common;

import java.net.InetSocketAddress;
import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.protobuf.GeneratedMessage;
import com.game.core.net.message.AbstractCustomizeMessage;
import com.game.core.net.message.InternalMessage;
import com.kodgames.core.net.udp.udpkit.UdpConnection;
import com.game.core.session.Session;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.util.AttributeKey;

public class RemoteNode
{
	private static final Logger logger = LoggerFactory.getLogger(RemoteNode.class);
	
	public static final AttributeKey<RemoteNode> REMOTENODE = AttributeKey.valueOf("remoteNode");
	public static final AttributeKey<Integer> CHANNELID = AttributeKey.valueOf("channelId");
	
	private InetSocketAddress address;
	private Channel channel;
	private long userId;
	private CorgiUID corgiUID = new CorgiUID();
	private Object attachement;
	//BattleServer会需要该字段
	private Long instanceId = null;
	private long roleId = 0;
	private long accountId;
	private int sessionId;
	
	private boolean isUdp = false;
	private UdpConnection udpCn;
	private int channelId = 1;		//udp发送消息时，使用的channelId，缺省channelId为1
	
	private long recvProtoAmount = 0;
	private long sendProtoAmount = 0;
	
	private EncryptCoder encryptCoder = new EncryptCoder();
	
	Session session = null;
	
	private String accountID;
	private int serverID;
	private int ID; // playerID
	private int roomID;
	private Date loginTime = null;
	private int clientChannel = 0;
	
	public RemoteNode()
	{
		this.isUdp = false;
	}
	
	public RemoteNode(UdpConnection cn)
	{
		this.isUdp = true;
		this.udpCn = cn;
		this.address = cn.getEndPoint();
	}
	
	public void setRoomID(int roomID)
	{
		this.roomID = roomID;
	}
	
	public int getRoomID()
	{
		return roomID;
	}
	
	public void setID(int ID)
	{
		this.ID = ID;
	}
	
	public int getID()
	{
		return ID;
	}
	
	public void setAccountID(String accountID)
	{
		this.accountID = accountID;
	}
	
	public String getAccountID()
	{
		return accountID;
	}

	public void setServerID(int serverID)
	{
		this.serverID = serverID;
	}
	
	public int getServerID()
	{
		return serverID;
	}

	public Object getAttachement()
	{
		return attachement;
	}

	public void setAttachement(Object attachement)
	{
		this.attachement = attachement;
	}

	public Long getInstanceId()
	{
		return instanceId;
	}

	public void setInstanceId(Long instanceId)
	{
		this.instanceId = instanceId;
	}

	public long getRoleId()
	{
		return roleId;
	}

	public void setRoleId(long roleId)
	{
		this.roleId = roleId;
	}

	public InetSocketAddress getAddress()
	{
		return address;
	}

	public void setAddress(InetSocketAddress address)
	{
		this.address = address;
	}

	public Channel getChannel()
	{
		return channel;
	}

	public Object getKey()
	{
//		if(isUdp)
//			return address;
		
		return accountID;
	}
	public void setChannel(Channel channel)
	{
		this.channel = channel;
	}

	public long getUserId()
	{
		return userId;
	}

	public void setUserId(long id)
	{
		this.userId = id;
	}

	public CorgiUID getCorgiUID()
	{
		return corgiUID;
	}

	public void setCorgiUID(CorgiUID corgiUID)
	{
		thisUID = corgiUID;
	}
	
	public Session getSession()
	{
		return session;
	}

	public void setSession(Session session)
	{
		this.session = session;
	}

	public void incRecvAmount()
	{
		this.recvProtoAmount += 1;
		if(session != null)
			session.setUpSequenceId(recvProtoAmount);
	}
	
	public long getRecvAmount()
	{
		return this.recvProtoAmount;
	}
	
	public void incSendAmount()
	{
		this.sendProtoAmount += 1;
		if(session != null)
			session.setDownSequenceId(sendProtoAmount);
	}
	
	public long getSendAmount()
	{
		return this.sendProtoAmount;
	}
	
	public void clearProtocolAmount()
	{
		this.sendProtoAmount = 0;
		this.recvProtoAmount = 0;
		session.setUpSequenceId(0);
		session.setDownSequenceId(0);
	}

	public long getAccountId()
	{
		return accountId;
	}

	public void setAccountId(long accountId)
	{
		this.accountId = accountId;
	}

	public int getSessionId()
	{
		return sessionId;
	}

	public void setSessionId(int sessionId)
	{
		this.sessionId = sessionId;
	}

	public void write(AbstractCustomizeMessage msg)
	{
		InternalMessage p = new InternalMessage();
		p.setMessage(msg);
		p.setProtocolID(msg.getProtocolID());
		p.setCallback(msg.getCallback());
		if (isUdp)
		{
			p.setRemoteNode(this);
			p.setChannelId(channelId);
		}
		channel.write(p);
	}

	public void write(int callback, int protocolID, byte[] buffer)
	{
		InternalMessage p = new InternalMessage();
		p.setMessage(buffer);
		p.setProtocolID(protocolID);
		p.setCallback(callback);
		if (isUdp)
		{
			p.setRemoteNode(this);
			p.setChannelId(channelId);
		}
		channel.write(p);
	}
	
	public void write(int callback, int protocolID, GeneratedMessage msg)
	{
		InternalMessage p = new InternalMessage();
		p.setMessage(msg);
		p.setProtocolID(protocolID);
		p.setCallback(callback);
		if (isUdp)
		{
			p.setRemoteNode(this);
			p.setChannelId(channelId);
		}
		channel.write(p);
	}

	public void flush()
	{
		channel.flush();
	}

	public void writeAndFlush(AbstractCustomizeMessage msg)
	{
		if(msg == null)
			throw new NullPointerException();
		InternalMessage p = new InternalMessage();
		p.setMessage(msg);
		p.setProtocolID(msg.getProtocolID());
		p.setCallback(msg.getCallback());

		if (isUdp)
		{
			p.setRemoteNode(this);
			p.setChannelId(channelId);
		}
 		channel.writeAndFlush(p);
	}

	public void writeAndFlush(int callback, int protocolID, byte[] buffer)
	{
		InternalMessage p = new InternalMessage();
		p.setMessage(buffer);
		p.setProtocolID(protocolID);
		p.setCallback(callback);
		if (isUdp)
		{
			p.setRemoteNode(this);
			p.setChannelId(channelId);
		}
		channel.writeAndFlush(p);
	}
	
	public void writeAndFlush(int callback, int protocolID, GeneratedMessage msg)
	{
		InternalMessage p = new InternalMessage();
		p.setMessage(msg);
		p.setProtocolID(protocolID);
		p.setCallback(callback);
		if (isUdp)
		{
			p.setRemoteNode(this);
			p.setChannelId(channelId);
		}
		channel.writeAndFlush(p);
	}

	public void writeAndClose(AbstractCustomizeMessage message)
	{
		if(message == null)
			throw new NullPointerException();
		InternalMessage p = new InternalMessage();
		p.setMessage(message);
		p.setProtocolID(message.getProtocolID());
		p.setCallback(message.getCallback());
		if (isUdp)
		{
			p.setRemoteNode(this);
			p.setChannelId(channelId);
		}
		ChannelFuture f = channel.writeAndFlush(p);
		f.addListener(futureListner);
	}
	
	public void writeAndClose(int callback, int protocolID, GeneratedMessage msg)
	{
		InternalMessage p = new InternalMessage();
		p.setMessage(msg);
		p.setProtocolID(protocolID);
		p.setCallback(callback);
		if (isUdp)
		{
			p.setRemoteNode(this);
			p.setChannelId(channelId);
		}
		ChannelFuture f = channel.writeAndFlush(p);
		f.addListener(futureListner);
	}
	
	public void writeAndClose(int callback, int protocolID, byte[] buffer)
	{
		InternalMessage p = new InternalMessage();
		p.setMessage(buffer);
		p.setProtocolID(protocolID);
		p.setCallback(callback);
		if (isUdp)
		{
			p.setRemoteNode(this);
			p.setChannelId(channelId);
		}
		ChannelFuture f = channel.writeAndFlush(p);
		f.addListener(futureListner);
	}
		
	private static ChannelFutureListener futureListner = new ChannelFutureListener()
	{
		@Override
		public void operationComplete(ChannelFuture future)
		{
			Channel ch = future.channel();
			ch.close().addListener(ChannelFutureListener.CLOSE);
		}
	};
	
	public boolean isConnected()
	{
		return this.channel.isActive();
	}
	
	public void close()
	{
		logger.debug("RemoteNode{}.close be called.", this.address);
		if (isUdp)
		{
			this.udpCn.disconnect();
			roleId = 0;
		}
		else
		{
			this.channel.close();
		}
	}
	
	public EncryptCoder getEncryptCoder()
	{
		return this.encryptCoder;
	}

	public Date getLoginTime()
	{
		return loginTime;
	}

	public void setLoginTime(Date loginTime)
	{
		this.loginTime = loginTime;
	}

	public int getClientChannel()
	{
		return clientChannel;
	}

	public void setClientChannel(int clientChannel)
	{
		this.clientChannel = clientChannel;
	}
}
